﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace DynaPrecision.Financial
{
	public class TrinomialTreeNonPathDependent : OptionPricingDataHolder
	{
		MemorylessTimeStepHolder _dataHolder = new MemorylessTimeStepHolder();
		public void Initiate()
		{
			_underlying = DynaPrecision.ListGenerator.ConstantValueList<double>(CurrentPrice, TimeDivisionCount * 2 + 1).ToList();
			for (int i = 1; i < TimeDivisionCount + 1; i++)
			{
				_underlying[TimeDivisionCount + i] *= Math.Pow(UpFactor, i);
				_underlying[TimeDivisionCount - i] /= Math.Pow(UpFactor, i);
			}
			_dataHolder.DataSize = TimeDivisionCount * 2 + 1;
			_dataHolder.Initiate();
			_dataHolder.CurrentTimeStep[TimeDivisionCount] = _payoffFunctor.Payoff(_underlying[TimeDivisionCount], Expiration);
			for (int i = 1; i < TimeDivisionCount + 1; i++)
			{
				_dataHolder.CurrentTimeStep[TimeDivisionCount + i] = _payoffFunctor.Payoff(_underlying[i + TimeDivisionCount], Expiration);
				_dataHolder.CurrentTimeStep[TimeDivisionCount - i] = _payoffFunctor.Payoff(_underlying[TimeDivisionCount - i], Expiration);
			}
			CurrentDiscountIndex = 0;
		}
		public int CurrentDiscountIndex;
		public double CurrentTime
		{
			get
			{
				return this.Expiration - TimeDivision * CurrentDiscountIndex;
			}
		}
		public void AdvanceToFinish()
		{
			for (int i = 0; i < TimeDivisionCount; i++)
			{
				Advance();
			}
		}
		public double Result
		{
			get
			{
				return _dataHolder.CurrentTimeStep[TimeDivisionCount];
			}
		}
		public void Advance()
		{
			CurrentDiscountIndex++;
			_dataHolder.NextTimeStep[TimeDivisionCount] = (UpPossibility * _dataHolder.CurrentTimeStep[TimeDivisionCount + 1] +
					 DownPossibility * _dataHolder.CurrentTimeStep[TimeDivisionCount - 1] +
					 StayPossibility * _dataHolder.CurrentTimeStep[TimeDivisionCount]) * Math.Exp(-RiskFreeInterestRate * TimeDivision);
			if (IsAmerican)
			{
				_dataHolder.NextTimeStep[TimeDivisionCount] = _dataHolder.NextTimeStep[TimeDivisionCount] > _payoffFunctor.Payoff(_underlying[TimeDivisionCount], CurrentTime) ?
					_dataHolder.NextTimeStep[TimeDivisionCount] : _payoffFunctor.Payoff(_underlying[TimeDivisionCount], CurrentTime);
			}
			for (int i = 1; i < TimeDivisionCount - CurrentDiscountIndex + 1; i++)
			{
				int k = TimeDivisionCount + i;
				_dataHolder.NextTimeStep[k] = (UpPossibility * _dataHolder.CurrentTimeStep[k + 1] +
					DownPossibility * _dataHolder.CurrentTimeStep[k - 1] +
					StayPossibility * _dataHolder.CurrentTimeStep[k]) * Math.Exp(-RiskFreeInterestRate * TimeDivision);
				if (IsAmerican)
				{
					_dataHolder.NextTimeStep[k] = _dataHolder.NextTimeStep[k] > _payoffFunctor.Payoff(_underlying[k], CurrentTime) ?
						_dataHolder.NextTimeStep[k] : _payoffFunctor.Payoff(_underlying[k], CurrentTime);
				}

				k = TimeDivisionCount - i;
				_dataHolder.NextTimeStep[k] = (UpPossibility * _dataHolder.CurrentTimeStep[k + 1] +
					DownPossibility * _dataHolder.CurrentTimeStep[k - 1] +
					StayPossibility * _dataHolder.CurrentTimeStep[k]) * Math.Exp(-RiskFreeInterestRate * TimeDivision);
				if (IsAmerican)
				{
					_dataHolder.NextTimeStep[k] = _dataHolder.NextTimeStep[k] > _payoffFunctor.Payoff(_underlying[k], CurrentTime) ?
						_dataHolder.NextTimeStep[k] : _payoffFunctor.Payoff(_underlying[k], CurrentTime);
				}
			}
			_dataHolder.Advance();
		}
		protected IPayoff _payoffFunctor = null;
		public IPayoff PayoffFunctor
		{
			get
			{
				return _payoffFunctor;
			}
			set
			{
				this._payoffFunctor = value;
				_payoffFunctor.DataHolder = this as OptionPricingDataHolder;
			}
		}
		protected List<double> _underlying = null;
		public virtual int TimeDivisionCount
		{
			get;
			set;
		}
		public virtual double TimeDivision
		{
			get
			{
				return Expiration / TimeDivisionCount;
			}
		}
		public virtual double Niu
		{
			get
			{
				return RiskFreeInterestRate - ContinuousYieldRate - 0.5 * Volatility * Volatility;
			}
		}
		public virtual double DeltaX
		{
			get
			{
				return Volatility * Math.Sqrt(3.0 * TimeDivision);
			}
		}
		public virtual double Alpha
		{
			get
			{
				return Niu * TimeDivision / DeltaX;
			}
		}
		public virtual double Beta
		{
			get
			{
				return (Volatility * Volatility * TimeDivision + Niu * Niu * TimeDivision) / DeltaX / DeltaX;
			}
		}
		public virtual double UpFactor
		{
			get
			{
				return Math.Exp(DeltaX);
			}
		}
		public virtual double DownFactor
		{
			get
			{
				return 1.0 / UpFactor;
			}
		}
		public virtual double UpPossibility
		{
			get
			{
				return 0.5 * (Alpha + Beta);
			}
		}
		public virtual double DownPossibility
		{
			get
			{
				return 0.5 * (Beta - Alpha);
			}
		}
		public virtual double StayPossibility
		{
			get
			{
				return 1 - Beta;
			}
		}
	}
}
